ts是物件導向語言,因此要好好利用它,就是要將相關聯的變數、方法都群組在一起,也就是使用class。
延續座標的例子,由於變數x, y及方法drawPoint、getDistance都是與Point有關,因此創造一個名為Point的class來放這些東西。
class Point{
x:number, //稱為field
y:number,
draw(){ //稱為method
console.log('x:',this.x,' y:',this.y);
}
getDistance(){
//...
}
}
有了名為Point的class後,就可以建立一個Point的instance(中文要叫甚麼呢?實體嗎?),就叫做point1吧,並且使用Point有的方法了
let point1 = new Point; // point1就是依據class Point建立的instance
point1.draw(); //x:undefine,y:undefine (´⊙ω⊙`)
不過,在定義class時設定了fields x & y和method draw,可是我們沒有給x, y值,也沒有限制一定要給值,為了避免錯誤,就有了constructor
在定義class Point時加入constructor,於是每當建立Point的instance時,就可以在()內給予所需的參數們。
class Point{
x:number, //稱為field
y:number,
constructor(x:number, y:number){
this.x = x;
this.y = y;
}
draw(){ //稱為method
console.log('x:',this.x,' y:',this.y);
}
}
有了constructor後,我們要創建instance時,如果不給參數就會有錯誤訊息,有那麼一些些防呆的效果
let point2 = new Point(8,24);
point2.draw(); //x:8,y:24 ヽ(✿゚▽゚)ノ
不過當然,人生就是有時候需要給參數、有時候又不用,所以在建立constructor時,加上一個問號?,生命就會多一點彈性:
class Point{
x:number, //稱為field
y:number,
constructor(x?:number, y?:number){
this.x = x;
this.y = y;
}
draw(){ //稱為method
console.log('x:',this.x,' y:',this.y);
}
getDistance(){
//...
}
}
let point2 = new Point();
有了?後,建立instance時沒給參數,typescript也不會生氣了。
當我們好不容易透過Point 創立了point2,它的座標應該在(8,24),可是當時光飛逝,有一天point2.x和point2.y可能不小心被改掉,於是point2已經不是當初我認識的那個point2了
let point2 = new Point(8,24);
point2.x=4;
point2.y=12;
point2.draw(); //x:4,y:12 Σ(;゚д゚)
如果不希望它改變,就需要在定義class時,將x & y 加入private
class Point{
private x:number, //用private把x & y藏得好好的
private y:number,
constructor(x:number, y:number){
this.x = x;
this.y = y;
}
draw(){ //稱為method
console.log('x:',this.x,' y:',this.y);
}
}
let point2 = new Point(8,24);
point2.x=4; //錯誤訊息
point2.y=12; //錯誤訊息
point2.draw(); //x:8,y:24 ヽ(✿゚▽゚)ノ
只有在建立instance時,可在()內給予參數,之後就不能改變了。
做到這裡,不覺得我們先定義了x ,y的型別、又在constructor再次指定x,y,總覺得很多餘啊,所以,當然就可以簡化啦~直接在constructor的參數中給予public或private,ts就會知道這兩位是此class的field了。
class Point{
constructor(public x:number,private y:number){
}
draw (){ //method
console.log('x:',this.x,' y:',this.y);
}
}
可是可是,用了private後,一旦建立一個Point並初始化x,y,一切就無法挽回、不能改變了嗎?
不不不,還是有方法的,在class Point多新增一個修改x,y的方法method就好(人類可真矛盾啊),好處就在於,此時就可以設定條件
,符合條件在能修改成功囉。
class Point{
constructor(public x:number,private y:number){
}
draw (){ //method
console.log('x:',this.x,' y:',this.y);
}
getX(){ //呼叫這個方法就能知道x值是甚麼
return this.x;
}
setX(value){ //呼叫此方法,設定value只要是大於0,就能改變x值更新成value。
if(value <= 0){
throw new Error('X不能小於0');
}else
this.x = value;
}
}
TS當然也知道大家有這種需求,所以其實get與set是個將X設為Point的property的語法。
get X(){ //稱作getter
return this.x;
}
set X(value){ //稱作setter
if(value <= 0){
throw new Error('X不能小於0');
}else
this.x = value;
}
有了get,就可以用point2.X
來取得this.x的值,.X
成為了point2的property。
有了set,就可以用point2.X = 4
來將this.x的值改為4,使用set,此property才能=某值喔。
當然,在這只是為了取得及重新設定座標x,才將property的名稱設為X,如果想設計其他property給Point,也是可以的啦
不過,明明我們給的field是小寫x,但當作property取得時卻是大寫X,這當然是因為如果都用小寫,就是重複名稱的變數了,但在命名原則中,只有特殊如class才能使用首字大寫,只是變數當然都用駝峰命名,於是依照慣例,像這種private變數,都會在前加底線_、例如_x,那麼設定對應property時,就可以用小寫x作為property名稱,使用上就比較不會搞混了
class Point{
constructor(private _x:number,private _y:number){
}
draw (){ //method
console.log('x:',this._x,' y:',this._y);
}
get x(){
return this._x;
}
set x(value){
if(value <= 0){
throw new Error('X不能小於0');
}else
this._x = value;
}
}
let point = new Point(1,2);
point.x; //使用getter
point.x = 10; //使用setter
身為物件導向語言,最喜歡一個積木、一個積木、最後組成城堡,當Point這塊積木常常被拿來蓋城堡,就會將它設計成量產的積木,而不是每一份城堡.ts文件中,都有一段一模一樣的class Point定義。
於是,新建一個叫做point.ts的檔案,將其export;在欲使用Point的ts檔案中import進去,就能達成此目的
//point.ts檔
export class Point{
constructor(private _x:number,private _y:number){
}
draw (){ //method
console.log('x:',this._x,' y:',this._y);
}
get x(){
return this._x;
}
set x(value){
if(value <= 0){
throw new Error('X不能小於0');
}else
this._x = value;
}
}
//欲使用Point的ts檔
import { Point } from './point'; //from相對路徑,後面沒有.ts喔
let point = new Point(1,2);
point.x; //使用getter
point.x = 10; //使用setter